import os
import subprocess
import sys
import time
from datetime import datetime

import paramiko


class InstallDb:
    def __init__(self, **kwargs):
        self.u_pwd = kwargs['userpasswd']
        self.r_pwd = kwargs['rootpasswd']
        self.db_pwd = kwargs['dburpasswd']
        self.report_db_pwd = kwargs['report_db_passwd']
        self.report_db_host = kwargs['report_db_host']
        self.report_user = kwargs['report_user']
        self.report_db = kwargs['report_db']
        self.report_db_port = kwargs['report_db_port']
        self.db_version = kwargs['DB_RELEASE_VERSION']
        self.ftp_path = kwargs['ftp_path']
        self.func_user_prefix = kwargs['func_user_prefix']
        self.prefixPort = kwargs['prefixPort']
        self.db_root_path = kwargs['db_root_path']
        self.install_pkg_root_path = kwargs['install_pkg_root_path']
        self.feature = kwargs['feature']
        self.date = datetime.strftime(datetime.now(), '%m%d')
        self.init_num = str(int(self.date) +
                            int(kwargs['BUILD_NUMBER'])).rjust(4, '0')
        self.user_name = f"{self.func_user_prefix}{self.init_num}"
        self.port = f'{self.prefixPort}{self.init_num}'
        self.pkg_path = os.path.join(self.install_pkg_root_path,
                                     self.user_name, self.date, 'pkg')
        self.script_path = os.path.join(self.pkg_path, 'script')
        self.xml_path = os.path.join(self.pkg_path, f'{self.user_name}.xml')
        self.env_path = os.path.join('/home', self.user_name,
                                     'gaussdb.bashrc')

    @staticmethod
    def print_log(msg: str):
        now_time = datetime.strftime(datetime.now(), '%Y-%m-%d %H:%M:%S')
        print(f'[{now_time}] {msg}')

    @staticmethod
    def ssh_connect(ip, username, pwd):
        ssh = paramiko.SSHClient()
        ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
        ssh.connect(ip, 22, username, pwd, timeout=1800)
        return ssh

    def ssh_run(self, ssh, cmd):
        self.print_log(cmd)
        stdin, stdout, stderr = ssh.exec_command(cmd)
        run_res = stdout.read().decode('utf-8').strip()
        er_res = stderr.read().decode('utf-8').strip()
        if er_res:
            self.print_log(f'error: {er_res}')
        return run_res

    @staticmethod
    def ssh_close(ssh):
        ssh.close()

    def create_trust(self, script_path, f_path):
        cmd = f'cd {script_path} && ./gs_sshexkey -f {f_path}'
        self.print_log(cmd)
        res = subprocess.getoutput(cmd)
        self.print_log(res)
        assert 'Successfully created SSH trust' in res

    def clean_env(self, *args):
        self.print_log('开始清理环境')
        for i in args:
            self.print_log(i)
            ssh = self.ssh_connect(i, 'root', self.r_pwd)
            cmd = f"cat /etc/passwd | grep {self.func_user_prefix}| " \
                f"awk -F: '$3>=500' | cut -f 1 -d :"
            res = self.ssh_run(ssh, cmd)
            self.print_log(f'res: {res}')
            for old_u in res.splitlines():
                self.print_log(f'先删除用户{old_u}的定时任务')
                del_cmd = f'su - {old_u} -c "crontab -r"'
                run_res = self.ssh_run(ssh, del_cmd)
                self.print_log(f'run_res: {run_res}')

                self.print_log(f'再杀死用户{old_u}的进程')
                kill_cmd = f"\nps -u {old_u}|grep -v PID|" \
                    f"awk '{{print $1}}'|xargs -I {{}} kill -9 {{}} " \
                    f"&& userdel -r {old_u}"
                try:
                    run_res = self.ssh_run(ssh, kill_cmd)
                    self.print_log(f'run_res: {run_res}')
                except Exception as e:
                    self.print_log(str(e))
                    time.sleep(3)
                    run_res = self.ssh_run(ssh, kill_cmd)
                    self.print_log(f'run_res: {run_res}')
                finally:
                    q_cmd = f"\nps -o ruser=userForLongName -e -o pid,ppid," \
                        f"c,stime,tty,time,cmd|grep -v grep|grep {old_u}"
                    run_res = self.ssh_run(ssh, q_cmd)
                    self.print_log(f'run_res: {run_res}')

                self.print_log(f'最后清理用户{old_u}的目录')
                cmd += f"\nrm -rf /home/{old_u} /root/gauss_om/{old_u} " \
                    f"{self.db_root_path}/{old_u} " \
                    f"{self.install_pkg_root_path}/{old_u} /dev/shm/*"
                for c in cmd.splitlines():
                    run_res = self.ssh_run(ssh, c)
                    self.print_log(f'run_res: {run_res}')

            self.print_log('环境准备：清理内存、信号量')
            cmd = f"\n\\cp -r /etc/hosts.bak /etc/hosts"
            cmd += f"\n\\cp -r /etc/profile.bak /etc/profile"
            cmd += f"\nsync"
            cmd += f"\necho 3 > /proc/sys/vm/drop_caches"
            cmd += f"\nipcs -m|awk '{{if($3~/^[0-9]+$/) {{print $2}}}}'|" \
                f"xargs -I {{}} ipcrm -m {{}}"
            cmd += f"\nipcs -s|awk '{{if($3~/^[0-9]+$/) {{print $2}}}}'|" \
                f"xargs -I {{}} ipcrm -s {{}}"

            for c in cmd.splitlines():
                run_res = self.ssh_run(ssh, c)
                self.print_log(f'run_res: {run_res}')

            for j in range(1000):
                cmd = f"ls /home|grep {self.user_name}|awk '{{print $3}}'"
                res = self.ssh_run(ssh, cmd)
                self.print_log(res)
                if res == self.user_name or len(res) == 0:
                    break
                else:
                    self.user_name = f"{self.func_user_prefix}" \
                        f"{str(int(self.date) + j * 100).rjust(4, '0')}"
            self.print_log(self.user_name)

            for j in range(1000):
                cmd = f"lsof -i:{self.port}; " \
                    f"lsof -i:{int(self.port) + 1};" \
                    f"lsof -i:{int(self.port) + 4};" \
                    f"lsof -i:{int(self.port) + 5}"
                res = self.ssh_run(ssh, cmd)
                self.print_log(res)
                if len(res) != 0:
                    self.port = f"{self.prefixPort}" \
                        f"{str(int(self.date) + j * 100).rjust(4, '0')}"
                else:
                    break
            self.print_log(self.port)

            self.ssh_close(ssh)

    def prepare_user(self):
        self.print_log('获取可用用户和端口，创建用户和组')
        cmd = f"groupadd {self.user_name}"
        cmd += f"\nuseradd -g {self.user_name} -d /home/{self.user_name} " \
            f"-m -s /bin/bash {self.user_name}"
        cmd += f"\necho {self.u_pwd} | passwd --stdin {self.user_name}"
        res = subprocess.getoutput(cmd)
        self.print_log(res)

        self.print_log('解决偶现groupadd新增用户后，丢失了.bashrc和.bash_profile的问题')
        cmd = f'ls -al /home/{self.user_name}|grep -w .bashrc'
        cmd1 = f'ls -al /home/{self.user_name}|grep -w .bash_profile'
        for c in (cmd, cmd1):
            self.print_log(c)
            res = subprocess.getoutput(c)
            self.print_log(res)
            if self.user_name not in res:
                cmd2 = f'cp /etc/skel/{c.split()[-1]} /home/{self.user_name}/'
                self.print_log(cmd2)
                res = subprocess.getoutput(cmd2)
                self.print_log(res)

    def get_pkg(self):
        self.print_log('获取安装包')
        res = subprocess.getoutput('cat /etc/system-release')
        res1 = subprocess.getoutput('arch')
        if 'CentOS' in res:
            os_version = "CentOS7.6"
        else:
            if 'x86_64' in res1:
                os_version = "OpenEuler20.03_X86"
            else:
                os_version = "OpenEuler20.03"
        cmd = f'mkdir -p {self.pkg_path};' \
            f'rm -rf {self.pkg_path}/*;' \
            f'cd {self.pkg_path} && ' \
            f'wget "{self.ftp_path}/{os_version}/' \
            f'openGauss_{self.db_version}_PACKAGES_RELEASE.tar.gz"'
        self.print_log(cmd)
        res = subprocess.getoutput(cmd)
        assert '100%' in res

        cmd = f"ls {self.pkg_path}|" \
            f"grep openGauss_{self.db_version}_PACKAGES_RELEASE.tar.gz"
        self.print_log(cmd)
        res = subprocess.getoutput(cmd).strip()
        assert res == f'openGauss_{self.db_version}_PACKAGES_RELEASE.tar.gz'

        cmd = f'cd {self.pkg_path} && ' \
            f'tar -zxf "openGauss_{self.db_version}_PACKAGES_RELEASE.tar.gz"'
        self.print_log(cmd)
        res = subprocess.getoutput(cmd)
        self.print_log(res)

        f_list = os.listdir(self.pkg_path)
        for f in f_list:
            if f.endswith('tar.gz') and f != f"openGauss_{self.db_version}" \
                    f"_PACKAGES_RELEASE.tar.gz":
                cmd = f'cd {self.pkg_path} && tar -zxf "{f}"'
                self.print_log(cmd)
                res = subprocess.getoutput(cmd)
                self.print_log(res)

    def prepare_xml(self, **kargs):
        self.print_log('开始准备xml')
        print(kargs)
        hostnames = ','.join(kargs.values())
        ips = ','.join(kargs.keys())
        base_path = os.path.join(self.db_root_path, self.user_name, 'cluster')
        xml = f'''<?xml version="1.0" encoding="UTF-8"?>
<ROOT>
    <CLUSTER>
        <PARAM name="clusterName" value="{self.user_name}"/>
        <PARAM name="nodeNames" value="{hostnames}"/>
        <PARAM name="backIp1s" value="{ips}"/>
        <PARAM name="gaussdbAppPath" value="{os.path.join(base_path, 
                                                          'app')}"/>
        <PARAM name="gaussdbLogPath" value="{os.path.join(base_path,
                                                          'gaussdb_log')}"/>
        <PARAM name="tmpMppdbPath" value="{os.path.join(base_path, 'tmp')}"/>
        <PARAM name="gaussdbToolPath" value="{os.path.join(base_path,
                                                           'tool')}"/>
        <PARAM name="corePath" value="{os.path.join(base_path, 'corefile')}"/>
        <PARAM name="clusterType" value="single-inst"/>
    </CLUSTER>
    <DEVICELIST>
        <DEVICE sn="{list(kargs.values())[0]}">
            <PARAM name="name" value="{list(kargs.values())[0]}"/>
            <PARAM name="azName" value="AZ1"/>
            <PARAM name="azPriority" value="1"/>
            <PARAM name="backIp1" value="{list(kargs.keys())[0]}"/>
            <PARAM name="sshIp1" value="{list(kargs.keys())[0]}"/>
            <PARAM name="dataNum" value="1"/>
            <PARAM name="dataPortBase" value="{self.port}"/>
            <PARAM name="dataPortStandby" value="{self.port}"/>
            <PARAM name="dataNode1" value="{os.path.join(base_path, 'dn1')},{
        list(kargs.values())[1]},{os.path.join(base_path, 'dn1')},{
        list(kargs.values())[2]},{os.path.join(base_path, 'dn1')}"/>
        </DEVICE>
        <DEVICE sn="{list(kargs.values())[1]}">
            <PARAM name="name" value="{list(kargs.values())[1]}"/>
            <PARAM name="azName" value="AZ1"/>
            <PARAM name="azPriority" value="1"/>
            <PARAM name="backIp1" value="{list(kargs.keys())[1]}"/>
            <PARAM name="sshIp1" value="{list(kargs.keys())[1]}"/>
        </DEVICE>
        <DEVICE sn="{list(kargs.values())[2]}">
            <PARAM name="name" value="{list(kargs.values())[2]}"/>
            <PARAM name="azName" value="AZ1"/>
            <PARAM name="azPriority" value="1"/>
            <PARAM name="backIp1" value="{list(kargs.keys())[2]}"/>
            <PARAM name="sshIp1" value="{list(kargs.keys())[2]}"/>
        </DEVICE>
    </DEVICELIST>
</ROOT>'''
        with open(self.xml_path, 'w', encoding='utf-8', errors='ignore') as f:
            f.write(xml)

    def preinstall(self):
        self.print_log('开始预安装')
        install_path = os.path.join(self.db_root_path, self.user_name)
        cmd = f'''mkdir -p {install_path}
            chown -R {self.user_name}:{self.user_name} {install_path}
            chmod -R 755 {install_path}
            chmod -R 755 {os.path.join(self.install_pkg_root_path,
                                       self.user_name)}
            rm -rf ~/.ssh/*
            source ~/.bashrc
            '''
        for c in cmd.splitlines():
            self.print_log(c)
            res = subprocess.getoutput(c)
            self.print_log(res)

        cmd = f'''cd {self.script_path}
            expect <<EOF
            set timeout 1200
            spawn ./gs_preinstall -U {self.user_name} -G {self.user_name} \
            -X {self.xml_path} --sep-env-file={self.env_path}
            expect {{
                "*(yes/no)?" {{ send "yes\\n"; exp_continue }}
                "*assword:" {{ send "{self.r_pwd}\\n" }}
            }}
            expect {{
                "*(yes/no)?" {{ send "yes\\n"; exp_continue }}
                "*assword:" {{ send "{self.u_pwd}\\n"; exp_continue }}
                "*assword:" {{ send "{self.u_pwd}\\n" }}
                eof
            }}''' + '\nEOF\n'
        self.print_log(cmd)
        res = subprocess.getoutput(cmd)
        self.print_log(res)
        assert 'Preinstallation succeeded' in res

    def install(self, host):
        self.print_log('开始安装')
        cmd = f'chmod 777 {self.xml_path}'
        self.print_log(cmd)
        res = subprocess.getoutput(cmd)
        self.print_log(res)

        ssh = self.ssh_connect(host, self.user_name, self.u_pwd)
        cmd = f'''source {self.env_path};
            expect <<EOF
            set timeout 1200
            spawn gs_install -X {self.xml_path} \
            --dn-guc=modify_initial_password=false
            expect {{
                "yes/no" {{ send "yes\\n"; exp_continue }}
                "*database:" {{ send "{self.u_pwd}\\n"; exp_continue }}
                "*database:" {{ send "{self.u_pwd}\\n" }}
                eof
            }}''' + '\nEOF\n'
        res = self.ssh_run(ssh, cmd)
        self.print_log(res)
        self.ssh_close(ssh)
        assert 'Successfully started cluster' in res
        assert 'Successfully installed application' in res

    def set_param(self, host):
        self.print_log('安装完成，配置参数')
        ssh = self.ssh_connect(host, self.user_name, self.u_pwd)
        cmd = f"source {self.env_path};"
        cmd += f"gs_guc set -N all -I all -c 'backtrace_min_messages=error'"
        cmd += f" && gs_guc set -N all -I all -c 'most_available_sync=on'"
        cmd += f" && gs_guc set -N all -I all -c 'shared_buffers=512MB'"
        cmd += f" && gs_guc set -N all -I all -c 'enable_global_syscache=on'"
        cmd += f" && gs_om -t restart"
        cmd += f" && gsql -d postgres -p {self.port} -c '" \
            f"create database tpccdb; " \
            f"create user tpccuser identified by \"{self.u_pwd}\"; " \
            f"grant all privileges to tpccuser;'"
        res = self.ssh_run(ssh, cmd)
        self.print_log(res)
        self.ssh_close(ssh)
        assert res.count('Failed instances: 0') == 4
        assert 'Successfully stopped cluster' in res
        assert 'Successfully started' in res
        assert 'CREATE DATABASE' in res
        assert 'CREATE ROLE' in res
        assert 'ALTER ROLE' in res

    def get_db_vesion(self, host):
        self.print_log('获取数据库版本信息')
        cmd = f'''su - {self.user_name} -c "source {self.env_path}; \
        gsql -d postgres -p {self.port} -c \\"select version();\\""|\
        awk 'NR==3' '''
        self.print_log(cmd)
        db_version = subprocess.getoutput(cmd)
        self.print_log(db_version)
        assert f'openGauss {self.db_version} build' in db_version
        install_time = datetime.strftime(datetime.now(), '%Y-%m-%d %H:%M:%S')
        if host == self.report_db_host:
            ssh = self.ssh_connect(self.report_db_host, self.report_user,
                                   self.report_db_pwd)
            cmd = f"source /home/{self.report_user}/gaussdb.bashrc"
            cmd += f"\ngsql -d {self.report_db} -p {self.report_db_port} " \
                f"-c \"" \
                f"delete from \\\"ReportApp_dbverison\\\" " \
                f"where feature='{self.feature}'; " \
                f"insert into \\\"ReportApp_dbverison\\\"" \
                f"(feature, db_version, install_time) values " \
                f"('{self.feature}', E'{db_version}'," \
                f" '{install_time}');\""
            res = self.ssh_run(ssh, cmd)
            self.print_log(res)
            self.ssh_close(ssh)

    def set_init_data(self, run_os, *schds):
        if len(schds) <= 0:
            self.print_log('无需初始化数据库中数据')
        else:
            if self.feature == 'master_function':
                his_t_prefix = "ReportApp_hismasfunc"
                run_t_prefix = "ReportApp_masfunc"
            elif self.feature == 'tier2_function':
                his_t_prefix = "ReportApp_histier2func"
                run_t_prefix = "ReportApp_tier2func"
            elif self.feature == 'tier3_function':
                his_t_prefix = "ReportApp_histier3func"
                run_t_prefix = "ReportApp_tier3func"
            else:
                his_t_prefix = "ReportApp_hisrelia"
                run_t_prefix = "ReportApp_relia"
            schd_list = [f"'{i}'" for i in schds]
            ssh = self.ssh_connect(self.report_db_host, self.report_user,
                                   self.report_db_pwd)
            self.print_log('转移数据到历史库')
            trans_sql = f"insert into \\\"{his_t_prefix}model\\\"(" \
                f"run_os, model_name, total_num, pass_num, fail_num, " \
                f"block_num, start_time, run_time, ip_name, core_num) " \
                f"select run_os, model_name, total_num, " \
                f"pass_num, fail_num, " \
                f"block_num, start_time, run_time, ip_name, core_num " \
                f"from \\\"{run_t_prefix}model\\\" " \
                f"where model_name in ({','.join(schd_list)}) " \
                f"and run_os='{run_os}';"

            trans_sql += f"insert into \\\"{his_t_prefix}info\\\"(" \
                f"run_os, model_name, tc_name, run_result, start_time, " \
                f"run_time, failed_reason, solution, problem_type, " \
                f"issue_no, develop_owner, test_owner, status, " \
                f"ip_name, jenkins_link, end_time, log_link) " \
                f"select run_os, model_name, tc_name, " \
                f"run_result, start_time, " \
                f"run_time, failed_reason, solution, problem_type, " \
                f"issue_no, develop_owner, test_owner, status, " \
                f"ip_name, jenkins_link, end_time, log_link " \
                f"from \\\"{run_t_prefix}info\\\" " \
                f"where model_name in ({','.join(schd_list)}) " \
                f"and run_os='{run_os}';"
            cmd = f"source /home/{self.report_user}/gaussdb.bashrc"
            cmd += f"\ngsql -d {self.report_db} -p {self.report_db_port} " \
                f"-c \"{trans_sql}\""
            res = self.ssh_run(ssh, cmd)
            self.print_log(res)

            self.print_log('连跑用例前将数据库中所有数据初始化')
            sql_cmd = f"update \\\"{run_t_prefix}model\\\" " \
                f"set pass_num=0, fail_num=0, block_num=total_num, " \
                f"start_time=(select now()), run_time='00:00:01' " \
                f"where run_os='{run_os}' and " \
                f"model_name in ({','.join(schd_list)});"
            sql_cmd += f"update \\\"{run_t_prefix}info\\\" " \
                f"set run_result='to', start_time=(select now()), " \
                f"run_time='00:00:01', failed_reason=null, " \
                f"solution=null, problem_type='用例问题', " \
                f"issue_no=null, develop_owner=null, status='未解决', " \
                f"jenkins_link=null, end_time=(select now()), " \
                f"log_link=null, is_autotest_found=null " \
                f"where run_os='{run_os}' and " \
                f"model_name in ({','.join(schd_list)});"
            cmd = f"source /home/{self.report_user}/gaussdb.bashrc"
            cmd += f"\ngsql -d {self.report_db} -p {self.report_db_port} " \
                f"-c \"{sql_cmd}\""
            res = self.ssh_run(ssh, cmd)
            self.print_log(res)
            self.ssh_close(ssh)

    def set_memcheck(self, *args):
        params = 'export ASAN_OPTIONS=\\"disable_coredump=0:' \
                 'unmap_shadow_on_exit=1:abort_on_error=1:' \
                 'log_path=$GAUSSLOG/memchk/asan/runlog:' \
                 'alloc_dealloc_mismatch=1:fast_unwind_on_fatal=1:' \
                 'detect_leaks=1\\"\n' \
                 'export LSAN_OPTIONS=exitcode=0'
        cmd = f'source {self.env_path};' \
            f'mkdir -p $GAUSSLOG/memchk/asan/runlog;' \
            f'echo "{params}">>{self.env_path}'
        for i in args:
            ssh = self.ssh_connect(i, self.user_name, self.u_pwd)
            res = self.ssh_run(ssh, cmd)
            self.print_log(res)
            self.ssh_close(ssh)


if __name__ == '__main__':
    if len(sys.argv) < 24:
        raise Exception('请检查入参！')
    print(f'***************{sys.argv[0]}开始执行****************')
    param = dict()
    param['userpasswd'] = sys.argv[1]
    param['rootpasswd'] = sys.argv[2]
    param['dburpasswd'] = sys.argv[3]
    param['report_db_host'] = sys.argv[4]
    param['report_user'] = sys.argv[5]
    param['report_db_port'] = sys.argv[6]
    param['report_db'] = sys.argv[7]
    param['report_db_passwd'] = sys.argv[8]
    param['DB_RELEASE_VERSION'] = sys.argv[9]
    param['ftp_path'] = sys.argv[10]
    param['func_user_prefix'] = sys.argv[11]
    param['prefixPort'] = sys.argv[12]
    param['db_root_path'] = sys.argv[13]
    param['install_pkg_root_path'] = sys.argv[14]
    param['BUILD_NUMBER'] = sys.argv[21]
    model_names = sys.argv[22]
    param['feature'] = sys.argv[23]
    for k, v in param.items():
        if 'passwd' in k:
            print(f"参数{k}: 值******")
        else:
            print(f"参数{k}: 值{v}")

    ip_tuple = (f'{sys.argv[15]}', f'{sys.argv[16]}', f'{sys.argv[17]}')
    hostname_tuple = (f'{sys.argv[18]}', f'{sys.argv[19]}', f'{sys.argv[20]}')

    hosts = dict(zip(ip_tuple, hostname_tuple))
    ins = InstallDb(**param)
    cur_arch = subprocess.getoutput("arch|awk -F '_' '{print $1}'")
    cur_os = subprocess.getoutput("cat /etc/system-release|awk '{print $1}'")
    ins.set_init_data(f'{cur_arch}_{cur_os}', *model_names.split())

    ins.clean_env(*ip_tuple)
    ins.prepare_user()
    ins.get_pkg()
    ins.prepare_xml(**hosts)
    ins.preinstall()
    ins.install(ip_tuple[-1])
    ins.set_param(ip_tuple[0])
    ins.get_db_vesion(ip_tuple[-1])
    ins.set_memcheck(*ip_tuple)
    print(f'***************{sys.argv[0]}执行完成****************')
